package com.lft.queue02.circle;

/**
 * 数组模拟环形队列
 * 普通队列有一个缺点，使用一次就不能再使用。没有达到利用的效果。
 * 将普通队列，使用算法，改进成一个环形的队列。用到取模(%)
 * <p>
 * 思路说明：
 * 1) front 变量的含义做一个调整：front就指向队列的第一个元素。也就是说arr[front]就是队列的第一个元素。
 * front 初始值为0
 * 2) rear 变量的含义做一个调整：rear [>>指向队列的最后一个元素的后一个位置<<]，因为希望空出一个空间做为约定。
 * rear 初始值班为0
 * <p>
 * 3) 当队列满的条件： (rear + 1) % maxSize == front
 * <p>
 * 4) 队列为空的条件： rear == front
 * <p>
 * 5) 队列中有效数据的个数：(rear + maxSize - front) % maxSize	-> rear = 1 front = 0
 */
public class CircleArrayQueue {
	/**
	 * 表示数组的最大容量。
	 */
	private int maxSize;
	/**
	 * front 变量的含义做一个调整：front就指向队列的第一个元素。也就是说arr[front]就是队列的第一个元素。
	 * front 初始值为0
	 */
	private int front;
	/**
	 * rear 变量的含义做一个调整：rear 指向队列的最后一个元素的后一个位置，因为希望空出一个空间做为约定。
	 * rear 初始值班为0
	 */
	private int rear;
	/**
	 * 用于存放数据的数组，模拟队列
	 */
	private Object[] arr;
	
	/**
	 * 创建一个明确容量的队列
	 * @param maxSize 队列容量大小
	 */
	public CircleArrayQueue(int maxSize) {
		this.maxSize = maxSize;
		arr = new Object[maxSize];
	}
	
	/**
	 * 判断环形队列是否已满
	 * @return true 满，false 不满
	 */
	public boolean isFUll() {
		return (rear + 1) % maxSize == front;
	}
	
	/**
	 * 判断环形队列是否为空
	 * @return true 空，false 非空
	 */
	public boolean isEmpty() {
		return rear == front;
	}
	
	/**
	 * 添加数据到队列(进队列)
	 * @param n 需要添加的数据
	 */
	public void addQueue(Object n) {
		// 判断队列是否已满
		if (isFUll()) {
			System.out.println("队列满了，不能添加数据~~");
			return;
		}
		// 将数据添加进队列
		arr[rear] = n;
		// 让rear后移，这里必须考虑取模。这样方便指到数组的前面去。
		// (rear + 1) % maxSize 这个算法，在环形队列中，可以反复向后移一位
		rear = (rear + 1) % maxSize;
	}
	
	/**
	 * 获取队列中的数据(出队列)。
	 * @return 取出当前队列中的头数据。
	 */
	public Object getQueue() {
		// 判断队列是否为空
		if (isEmpty()) {
			throw new RuntimeException("队列为空，不能取数据");
		}
		
		// 这里需要分析出front是指向队列的第一个元素
		// 1. 先把front对应的值保留到一个临时变量。
		// 2. 将front后移，考虑取模
		// 3. 将临时保存的变量返回
		Object value = arr[front];
		// 获取完队列中的数据将当前索引下的数组元素置空，防止内存泄漏。
		arr[front] = null;
		//(front + 1) % maxSize这个算法，在环形队列中，可以反复向后移一位
		front = (front + 1) % maxSize;
		return value;
	}
	
	/**
	 * 显示队列中的所有数据
	 */
	public void showQueue() {
		// 判断队列是否为空
		if (isEmpty()) {
			System.out.println("队列为空，没有数据~~");
			return;
		}
		
		// 思路：从front开始遍历，遍历多少个元素
		// 思考
		for (int i = front; i < front + size(); i++) {
			System.out.printf("arr[%d] = %s\n", i % maxSize, arr[i % maxSize]);
		}
	}
	
	/**
	 * 求出当前队列有效数据的个数
	 */
	public int size() {
		// (rear + maxSize - front) % maxSize 这个算法是在求环形队列的有效数据个数
		return (rear + maxSize - front) % maxSize;
	}
	
	
	/**
	 * 显示队列的头数据，注意不是取出数据。
	 * @return 显示头数据
	 */
	public Object headQueue() {
		// 判断队列是否为空
		if (isEmpty()) {
			throw new RuntimeException("队列为空，不能取数据");
		}
		return arr[front];
	}
}
